//go:build linux
// +build linux

/*
目前linux网卡相关，只适配了 linaro 系统, 类debian系统，以修改 /etc/network/interfaces 方式操作
ubuntu18.04以上使用了 netplan 管理，未适配
*/

package network

import (
	"errors"
	"fmt"
	"go.uber.org/zap"
	"net"
	"onvif_server/pkg/log"
	"os"
	"regexp"
	"strings"
	"syscall"
)

func GetNetInterfaces() []NetInterface {
	res := make([]NetInterface, 0)
	ifcs := getIfcFromRouteWithGwAndMode()
	if ifcs == nil {
		return res
	}
	dns := getDns()
	for _, nifc := range ifcs {
		ifc, err := net.InterfaceByName(nifc.Name)
		if err != nil {
			continue
		}
		f := net.FlagBroadcast | net.FlagUp | net.FlagRunning | net.FlagMulticast
		nf := net.FlagLoopback | net.FlagPointToPoint
		if ifc.Flags&f == f && ifc.Flags&nf == 0 {
			if addrs, _err := ifc.Addrs(); _err == nil {
				for _, addr := range addrs {
					if inet, ok := addr.(*net.IPNet); ok {
						if v4 := inet.IP.To4(); v4 != nil {
							nifc.IP = v4.String()
							nifc.Mask = net.IPv4(inet.Mask[0], inet.Mask[1], inet.Mask[2], inet.Mask[3]).String()
							nifc.DNS = dns
							nifc.Ifc = ifc
							res = append(res, nifc)
						}
					}
				}
			}
		}
	}
	return res
}

func SetNetInterface(ifc NetInterface) error {
	file := "/etc/network/interfaces"
	fs, err := os.Stat(file)
	if err != nil {
		return err
	}
	err = syscall.Access(file, syscall.O_RDWR)
	if err != nil {
		return errors.New("没有权限读写该文件")
	}
	_raw, err := os.ReadFile(file)
	if err != nil {
		return err
	}
	raw := genNewConfContext(string(_raw), ifc)
	err = os.WriteFile(file, []byte(raw), fs.Mode().Perm())
	if err != nil {
		return err
	}
	//go func() {
	//	time.Sleep(time.Second)
	//	runCmd("init 6")
	//}()
	output, err := runCmd("systemctl restart networking")
	if err != nil {
		log.Log.Error("SetNetInterface error", zap.String("cmd", "systemctl restart networking"), zap.ByteString("output", output), zap.Error(err))
		return nil
	}
	return err
}

func genNewConfContext(raw string, ifc NetInterface) string {
	arr := strings.Split(raw, "\n")
	//先查找删除原配置
	startIdx := -1
	endIdx := -1
	//匹配 auto eth1
	autoMatchReg := "^ *auto *([^ ]+) *$"
	autoReg := regexp.MustCompile(autoMatchReg)
	for i := range arr {
		ok, _ := regexp.MatchString(autoMatchReg, arr[i])
		if ok {
			_auto := autoReg.FindAllStringSubmatch(arr[i], -1)
			fmt.Println(_auto[0][1])
			if _auto[0][1] == ifc.Name {
				startIdx = i
			} else {
				if startIdx > -1 {
					endIdx = i
					break
				}
			}
		}
	}
	ifcConf := genIfcConf(ifc)
	//没找到， 直接文件尾添加
	if startIdx == -1 {
		return strings.TrimSuffix(raw, "\n") + "\n" + ifcConf
	}
	if endIdx == -1 {
		endIdx = len(arr)
	}
	return strings.Join(append(arr[:startIdx], append([]string{ifcConf}, arr[endIdx:]...)...), "\n")
}

func genIfcConf(ifc NetInterface) string {
	ifcConf := make([]string, 0)
	ifcConf = append(ifcConf, "auto "+ifc.Name)
	if ifc.Mode == "dhcp" {
		ifcConf = append(ifcConf, "  iface "+ifc.Name+" inet dhcp")
	} else {
		ifcConf = append(ifcConf, "  iface "+ifc.Name+" inet static")
		ifcConf = append(ifcConf, "  address "+ifc.IP)
		ifcConf = append(ifcConf, "  netmask "+ifc.Mask)
		ifcConf = append(ifcConf, "  gateway "+ifc.Gateway)
		if ifc.DNS != "" {
			ifcConf = append(ifcConf, "  dns-nameservers "+ifc.DNS)
		}
	}
	return strings.Join(ifcConf, "\n") + "\n"
}

/*
*
cmd:ip -4 route
output:
default via 192.168.3.1 dev eth1 onlink
default via 192.168.2.1 dev eth0 proto dhcp metric 100
192.168.2.0/24 dev eth0 proto kernel scope link src 192.168.2.161 metric 100
192.168.3.0/24 dev eth1 proto kernel scope link src 192.168.3.203
*/
func getIfcFromRouteWithGwAndMode() []NetInterface {
	cmd := "ip -4 route"
	output, err := runCmd(cmd)
	if err != nil {
		log.Log.Error("getNetworkByHw error", zap.String("cmd", cmd), zap.ByteString("output", output), zap.Error(err))
		return nil
	}
	return parseIpRoute(string(output))
}
func parseIpRoute(output string) []NetInterface {
	res := make([]NetInterface, 0)
	gwReg := regexp.MustCompile(`default via ([0-9|.]+) dev ([^ ]+) .*`)
	arr := gwReg.FindAllStringSubmatch(string(output), -1)
	for i := range arr {
		m := NetInterface{
			Gateway: arr[i][1],
			Name:    arr[i][2],
			Mode:    STATIC,
		}
		if strings.Contains(arr[i][0], "proto dhcp") {
			m.Mode = DHCP
		}
		res = append(res, m)
	}
	return res
}

func getDns() string {
	res, err := os.ReadFile("/etc/resolv.conf")
	if err != nil {
		log.Log.Error("getDns error", zap.Error(err))
		return ""
	}
	dnsReg := regexp.MustCompile("\n *nameserver +([0-9|.]+).*")
	arr := dnsReg.FindAllStringSubmatch(string(res), -1)
	if len(arr) > 0 {
		return arr[len(arr)-1][1]
	}
	return ""
}
